local base = _G
local math = base.math
local setmetatable = base.setmetatable
module("gui")

MContainerG = {}
MContainerGMT = { __index = MContainerG }

setmetatable(MContainerG, { __index = ContainerG })

MContainerG.create = function(x, y, width, height, mt)
  local colors = get_colors('background')

  local self = Container(x, y, width, height, mt or MContainerGMT)
  
  self.colors = colors
  self.styles = { draggable = true }

  self.minx = nil
  self.maxx = nil
  self.snapx = nil
  self.miny = nil
  self.maxy = nil
  self.snapy = nil
  self.targx = nil
  self.targy = nil

  return self
end

MContainerG.destroy = function(self)
  self.colors = nil
  self.minx = nil
  self.maxx = nil
  self.snapx = nil
  self.miny = nil
  self.maxy = nil
  self.snapy = nil
  self.targx = nil
  self.targy = nil
  
  self.styles.draggable = nil
  ContainerG.destroy(self)
end

MContainerG.set_xlimits = function(self, minx, maxx, snapx)
  base.assert(minx <= maxx)
  self.minx = minx
  self.maxx = maxx
  self.snapx = snapx
end

MContainerG.set_ylimits = function(self, miny, maxy, snapy)
  base.assert(miny <= maxy)
  self.miny = miny
  self.maxy = maxy
  self.snapy = snapy
end

MContainerG.set_target = function(self, x, y)
  self.targx = x
  self.targy = y
end

MContainerG.get_target = function(self, x, y)
  return self.targx, self.targy
end

MContainerG.begin_drag = function(self, b, x, y)
  if self.focus then
    ContainerG.begin_drag(self, b, x, y)
    return
  end
  self.ox = x
  self.oy = y
end

MContainerG.dragging = function(self, b, x, y)
  if self.focus then
    ContainerG.dragging(self, b, x, y)
    return
  end
  if self.styles.draggable ~= true then
    return
  end
  local ox, oy = self:get_position()
  local nx = ox - (self.ox - x)
  local ny = oy - (self.oy - y)

  -- clamp to limits
  if self.minx then
    nx = math.max(nx, self.minx)
  end
  if self.maxx then
    nx = math.min(nx, self.maxx)
  end
  if self.miny then
    ny = math.max(ny, self.miny)
  end
  if self.maxy then
    ny = math.min(ny, self.maxy)
  end
  
  if nx ~= ox or ny ~= oy then
    self:set_position(nx, ny)
    self:on_dragging(nx, ny)
  end

  -- set target to the nearest snap
  if self.minx and self.maxx and self.snapx then
    if self.snapx > 1 then
      local sx = ox - self.minx
      local sw = (self.maxx - self.minx)/(self.snapx - 1)
      if sw > 0 and sx%sw ~= 0 then
        local tx = math.floor((sx + sw/2)/sw)*sw
        self.targx = tx + self.minx
      end
    end
  end
  if self.miny and self.maxy and self.snapy then
    if self.snapy > 1 then
      local sy = oy - self.miny
      local sh = (self.maxy - self.miny)/(self.snapy - 1)
      if sh > 0 and sy%sh ~= 0 then
        local ty = math.floor((sy + sh/2)/sh)*sh
        self.targy = ty + self.miny
      end
    end
  end
end

MContainerG.end_drag = function(self, b, x, y)
  if self.focus then
    ContainerG.end_drag(self, b, x, y)
    return
  end
  self.ox = nil
  self.oy = nil
end

MContainerG.update_position = function(self, dt)
  -- is the container being dragged?
  if self.ox or self.oy then
    return
  end
  -- slide to target
  if self.targx then
    local x, y = self:get_position()
    local tx = self.targx
    local dx = tx - x
    local nx = x + dx*10*dt
    if (dx > 1 and nx < tx) or (dx < -1 and nx > tx) then
      self:set_position(nx, y)
    else
      self:set_position(tx, y)
      self:on_arrivex(tx)
      self.targx = nil
    end
  end
  if self.targy then
    local x, y = self:get_position()
    local ty = self.targy
    local dy = ty - y
    local ny = y + dy*10*dt
    if (dy > 1 and ny < ty) or (dy < -1 and ny > ty) then
      self:set_position(x, ny)
    else
      self:set_position(x, ty)
      self:on_arrivey(ty)
      self.targy = nil
    end
  end
end

MContainerG.update = function(self, dt)
  ContainerG.update(self, dt)
  self:update_position(dt)
end

-- events
MContainerG.on_arrivex = function(self, x)

end

MContainerG.on_arrivey = function(self, y)

end

MContainerG.on_dragging = function(self, x, y)

end

MContainer = MContainerG.create